
package org.apache.ocean;

import java.util.concurrent.*;
import java.util.logging.*;
import java.util.*;

/**
 *
 * @author  jason rutherglen
 */
public class Cron {
  public static Logger log = Logger.getLogger(Cron.class.getName());
  public ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;
  
  public Cron(int poolSize) {
    scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(poolSize);
  }
  
  /**
   * Kills all tasks
   */
  public void shutdown() {
    BlockingQueue queue = scheduledThreadPoolExecutor.getQueue();
    Iterator iterator = queue.iterator();
    while (iterator.hasNext()) {
      ScheduledFuture future = (ScheduledFuture)iterator.next();
      
      future.cancel(false);
    }
    //scheduledThreadPoolExecutor.shutdown();
  }
  
  public class CronCallable implements Callable {
    public Callable callable;
    public CronType cronType;
    
    public CronCallable(CronType cronType, Callable callable) {
      this.cronType = cronType;
      this.callable = callable;
    }
    
    public Object call() throws Exception {
      scheduleNext();
      long startTime = System.currentTimeMillis();
      try {
        Object result = callable.call();
        return result;
      } finally {
        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;
        log.log(Level.INFO, "class: "+callable.getClass().getName()+" duration: "+duration);
      }
    }
    
    public void scheduleNext() {
      Date nowDate = new Date();
      
      long nextTime = cronType.nextTime(nowDate.getTime());
      Date nextDate = new Date(nextTime);
      long delay = nextTime = nextTime - nowDate.getTime();
      log.log(Level.INFO, "nowDate: "+nowDate+" nextDate: "+nextDate+" delay: "+delay);
      
      CronCallable cronCallable = new CronCallable(cronType, callable);
      
      ScheduledFuture scheduledFuture = scheduledThreadPoolExecutor.schedule(cronCallable, delay, TimeUnit.MILLISECONDS);
    }
  }
  
  public static class RunnableTask implements Runnable {
    Callable callable;
    
    public RunnableTask(Callable callable) {
      this.callable = callable;
    }
    
    public void run() {
      long startTime = System.currentTimeMillis();
      try {
        callable.call();
      } catch (Throwable ex) {
        log.log(Level.SEVERE, "", ex);
      } finally {
        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;
        log.log(Level.INFO, "class: "+callable.getClass().getName()+" duration: "+duration);
      }
    }
  }
  
  public void add(String time, Callable callable) throws Exception {
    time = time.trim().toLowerCase();
    if (time.endsWith("s")) {
      int delay = Integer.parseInt(time.substring(0, time.length()-1));
      
      log.log(Level.INFO, "scheduling seconds delay task: "+delay);
      
      ScheduledFuture scheduledFuture = scheduledThreadPoolExecutor.scheduleWithFixedDelay(new RunnableTask(callable), delay, delay, TimeUnit.SECONDS);
    } else {
      CronType cronType = new CronType();
      cronType.addText(time);
      CronCallable cronCallable = new CronCallable(cronType, callable);
      cronCallable.scheduleNext();
    }
  }
}
